namespace win {
    export class crypt32 {
        static encodeBin(txt: string, type: CRYPT_CONST) {
            let buflen = ffi.buff(1);
            this.crypt.CryptBinaryToStringA(txt, txt.length, type, null, buflen);
            let buff = ffi.buff(buflen[0]);
            this.crypt.CryptBinaryToStringA(txt, txt.length, type, buff, buflen);
            return ffi.string(buff, buflen[0])
        }

        static decodeBin(txt: string, type: CRYPT_CONST) {
            let buflen = ffi.buff(1);
            this.crypt.CryptStringToBinaryA(txt, txt.length, type, null, buflen, null, null);
            let buff = ffi.buff(buflen[0]);
            this.crypt.CryptStringToBinaryA(txt, txt.length, type, buff, buflen, null, null);
            return ffi.string(buff, buflen[0])
        }

        static toBase64A(txt: string) {
            return this.encodeBin(txt, CRYPT_CONST.CRYPT_STRING_BASE64);
        }
        static fromBase64A(txt: string) {
            return this.decodeBin(txt, CRYPT_CONST.CRYPT_STRING_BASE64);
        }
        static toHex(txt: string) {
            return this.encodeBin(txt, CRYPT_CONST.CRYPT_STRING_HEX);
        }
        static fromHex(txt: string) {
            return this.decodeBin(txt, CRYPT_CONST.CRYPT_STRING_HEX);
        }
        static toMD5(txt: string) {
            let pctx = ffi.new("MD5_CTX[1]");
            this.advapi32.MD5Init(pctx);
            this.advapi32.MD5Update(pctx, _G.tostring(txt), txt.length);
            this.advapi32.MD5Final(pctx);
            let md5str = ffi.string(pctx[0].digest, 16);
            return string.format("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", string.byte(md5str, 1, -1))
        }
    }

    export namespace crypt32 {
        ffi.cdef(
            `
            int  CryptBinaryToStringA(
                const char *pbBinary,
                int  cbBinary,
                int  dwFlags,
                char * pszString,
                int *pcchString
            );
            int CryptStringToBinaryA(
                const char *pszString,
                int  cchString,
                int  dwFlags,
                char *pbBinary,
                int  *pcbBinary,
                int  *pdwSkip,
                int  *pdwFlags
            );
            typedef struct {
                unsigned long i[2]; /* number of _bits_ handled mod 2^64 */
                unsigned long buf[4]; /* scratch buffer */
                unsigned char in[64]; /* input buffer */
                unsigned char digest[16]; /* actual digest after MD5Final call */
            } MD5_CTX;
            void   __stdcall MD5Init(MD5_CTX *);
            void   __stdcall MD5Update(MD5_CTX *, const char *, unsigned int);
            void   __stdcall MD5Final(MD5_CTX *);
        `
        )
        export let crypt = ffi.load("crypt32");
        export let advapi32 = ffi.load("advapi32");
    }

}


enum CRYPT_CONST {
    /**Base64, with certificate beginning and ending headers */
    CRYPT_STRING_BASE64HEADER = 0x00000000,
    /**Base64, without headers. */
    CRYPT_STRING_BASE64 = 0x00000001,
    /**Pure binary copy. */
    CRYPT_STRING_BINARY = 0x00000002,
    /**Base64, with request beginning and ending headers. */
    CRYPT_STRING_BASE64REQUESTHEADER = 0x00000003,
    /**Hexadecimal only. */
    CRYPT_STRING_HEX = 0x00000004,
    /**Hexadecimal, with ASCII character display. */
    CRYPT_STRING_HEXASCII = 0x00000005,
    /**Base64, with X.509 CRL beginning and ending headers. */
    CRYPT_STRING_BASE64X509CRLHEADER = 0x00000009,
    /**Hexadecimal, with address display. */
    CRYPT_STRING_HEXADDR = 0x0000000a,
    /**Hexadecimal, with ASCII character and address display. */
    CRYPT_STRING_HEXASCIIADDR = 0x0000000b,
    /**
     * A raw hexadecimal string.
     * Windows Server 2003 and Windows XP:  This value is not supported.
    */
    CRYPT_STRING_HEXRAW = 0x0000000c,
    /**
     * Enforce strict decoding of ASN.1 text formats. Some ASN.1 binary BLOBS can have the first few bytes of the BLOB incorrectly interpreted as Base64 text. In this case, the rest of the text is ignored. Use this flag to enforce complete decoding of the BLOB.
        Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP:  This value is not supported.
     */
    CRYPT_STRING_STRICT = 0x20000000,
    /**
     * Do not append any new line characters to the encoded string. The default behavior is to use a carriage return/line feed (CR/LF) pair (0x0D/0x0A) to represent a new line.
    Windows Server 2003 and Windows XP:  This value is not supported.
     */
    CRYPT_STRING_NOCRLF = 0x40000000,
    /**
     * Only use the line feed (LF) character (0x0A) for a new line. The default behavior is to use a CR/LF pair (0x0D/0x0A) to represent a new line.
     */
    CRYPT_STRING_NOCR = 0x80000000

}